{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "E:\\download\\anaconda\\download\\lib\\site-packages\\tensorflow_addons\\utils\\ensure_tf_install.py:53: UserWarning: Tensorflow Addons supports using Python ops for all Tensorflow versions above or equal to 2.6.0 and strictly below 2.9.0 (nightly versions are not supported). \n",
      " The versions of TensorFlow you are currently using is 2.4.1 and is not supported. \n",
      "Some things might work, some things might not.\n",
      "If you were to encounter a bug, do not file an issue.\n",
      "If you want to make sure you're using a tested and supported configuration, either change the TensorFlow version or the TensorFlow Addons's version. \n",
      "You can find the compatibility matrix in TensorFlow Addon's readme:\n",
      "https://github.com/tensorflow/addons\n",
      "  warnings.warn(\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2.4.1\n",
      "0.16.1\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "E:\\download\\anaconda\\download\\lib\\site-packages\\gensim\\similarities\\__init__.py:15: UserWarning: The gensim.similarities.levenshtein submodule is disabled, because the optional Levenshtein package <https://pypi.org/project/python-Levenshtein/> is unavailable. Install Levenhstein (e.g. `pip install python-Levenshtein`) to suppress this warning.\n",
      "  warnings.warn(msg)\n"
     ]
    }
   ],
   "source": [
    "import tensorflow as tf\n",
    "import tensorflow_addons as tfa\n",
    "print(tf.__version__)\n",
    "print(tfa.__version__)\n",
    "\n",
    "from tensorflow import keras\n",
    "from tensorflow.keras import layers, models\n",
    "from tensorflow.keras import backend as K\n",
    "\n",
    "from tensorflow.keras.preprocessing import sequence\n",
    "import numpy as np\n",
    "\n",
    "from sklearn.model_selection import train_test_split\n",
    "import matplotlib.pyplot as plt\n",
    "import gensim\n",
    "from gensim.models.keyedvectors import KeyedVectors\n",
    "\n",
    "plt.rcParams['font.sans-serif']=['simsun'] #用来正常显示中文标签\n",
    "plt.rcParams['axes.unicode_minus']=False #用来正常显示负号"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# w2v"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "w2v_path = \"../../../TCM embedding/result/TCM_w2v.txt\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "initial_vocab=[]\n",
    "vocab_emb = {}\n",
    "cnt = 0\n",
    "for line in open(w2v_path,encoding='UTF-8'):\n",
    "    if cnt != 0:\n",
    "        char_ls = line.split()\n",
    "        \n",
    "        initial_vocab.append(char_ls[0])\n",
    "        vocab_emb[char_ls[0]] = char_ls[1:]\n",
    "    else:\n",
    "        cnt += 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(vocab_emb) == len(initial_vocab)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "#读取所有数据生成字典\n",
    "def build_data(train_path,initial_vocab):\n",
    "    datas = []#【【句子】，【对应标签】】\n",
    "    #先加入特殊词\n",
    "    vocabs = {'UNK'}\n",
    "    #vocabs.add('PAD')\n",
    "    \n",
    "    data=[]\n",
    "    cate=[]\n",
    "    temp_data = []\n",
    "    temp_cate = []\n",
    "    for line in open(train_path,encoding='UTF-8'):\n",
    "        if line == '\\n' or line.split()[0] == \"。\":\n",
    "            if len(temp_data)!= 0:\n",
    "                data.append(temp_data)\n",
    "                cate.append(temp_cate)\n",
    "                temp_data = []\n",
    "                temp_cate = []\n",
    "        else:\n",
    "            line_ls = line.split()\n",
    "            char = line_ls[0].strip()\n",
    "            label = line_ls[1].strip()\n",
    "            temp_data.append(char)\n",
    "            temp_cate.append(label)\n",
    "            \n",
    "    for item in range(0,len(data)):\n",
    "        datas.append([data[item],cate[item]])\n",
    "        for i in data[item]:#数据中的词加入词库\n",
    "            vocabs.add(i)\n",
    "    for i in initial_vocab:#w2v中的词加入词库\n",
    "        vocabs.add(i)\n",
    "    \n",
    "    embeddings_matrix = np.zeros((len(vocabs), 200))# 初始化矩阵\n",
    "    \n",
    "    #索引和BIO标签对应\n",
    "    label=set()\n",
    "    for item in cate:\n",
    "        for i in item:\n",
    "            label.add(i)\n",
    "    idx2label = {idx: label for idx, label in enumerate(list(label))}\n",
    "    label2idx = {label: idx for idx,label  in idx2label.items()}\n",
    "    \n",
    "    #字符和索引编号对应\n",
    "    idx2vocab = {idx: char for idx, char in enumerate(list(vocabs))}\n",
    "    vocab2idx = {char: idx for idx, char in idx2vocab.items()}\n",
    "    \n",
    "    # 权重矩阵\n",
    "    for i in idx2vocab:\n",
    "        if idx2vocab[i] in initial_vocab:\n",
    "            embeddings_matrix[i] = vocab_emb[idx2vocab[i]]\n",
    "        \n",
    "    #self.write_file(list(vocabs), self.vocab_path)\n",
    "    return datas,label,idx2vocab,vocab2idx,idx2label,label2idx,embeddings_matrix"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "datas,label,idx2vocab,vocab2idx,idx2label,label2idx,embeddings_matrix = build_data('../../data/LAB1/corpus-buduan.txt',initial_vocab)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "#读取所有数据生成字典\n",
    "def load_data(train_path):\n",
    "    datas = []#【【句子】，【对应标签】】\n",
    "\n",
    "    \n",
    "    data=[]\n",
    "    cate=[]\n",
    "    temp_data = []\n",
    "    temp_cate = []\n",
    "    for line in open(train_path,encoding='UTF-8'):\n",
    "        if line == '\\n' or line.split()[0] == \"。\":\n",
    "            if len(temp_data)!= 0:\n",
    "                data.append(temp_data)\n",
    "                cate.append(temp_cate)\n",
    "                temp_data = []\n",
    "                temp_cate = []\n",
    "        else:\n",
    "            line_ls = line.split()\n",
    "            char = line_ls[0].strip()\n",
    "            label = line_ls[1].strip()\n",
    "            temp_data.append(char)\n",
    "            temp_cate.append(label)\n",
    "            \n",
    "    for item in range(0,len(data)):\n",
    "        datas.append([data[item],cate[item]])\n",
    "    \n",
    "    return datas"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "train = load_data('../../data/LAB1/train-buduan.txt')\n",
    "test = load_data('../../data/LAB1/test-buduan.txt')\n",
    "dev = load_data('../../data/LAB1/dev-buduan.txt')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "train.extend(dev)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "#按照索引建立训练数据\n",
    "def read_corpus(datas,vocab2idx,label2idx):\n",
    "    corpus, labels = [],[]\n",
    "    for item in datas:\n",
    "        sent_=item[0]\n",
    "        tag_=item[1]\n",
    "        sent_ids = [vocab2idx[char] if char in vocab2idx else vocab2idx['<UNK>'] for char in sent_]\n",
    "        tag_ids = [label2idx[label] if label in label2idx else 0 for label in tag_]\n",
    "        corpus.append(sent_ids)\n",
    "        labels.append(tag_ids)\n",
    "    return corpus,labels"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "X_train,y_train = read_corpus(train,vocab2idx,label2idx)\n",
    "X_test,y_test = read_corpus(test,vocab2idx,label2idx)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "555"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(X_train)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "144"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(X_test)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "length = [len(i) for i in X_train]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(array([128., 187., 120.,  68.,  26.,  11.,   3.,   2.,   7.,   3.]),\n",
       " array([  1.,  11.,  21.,  31.,  41.,  51.,  61.,  71.,  81.,  91., 101.]),\n",
       " <BarContainer object of 10 artists>)"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXMAAAD3CAYAAADv7LToAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8vihELAAAACXBIWXMAAAsTAAALEwEAmpwYAAANTUlEQVR4nO3dX2id933H8fe3xEGw2p5SHZBXfKb8qXG3gROmEGOw0uUmJm7tm4BHluBROsHYOpdusOzGV7vI2HaReK6HfZOALgbJ2CAJXT1ITLa4xXEE6RikauJ4m7fOa6N6qRuzUue7Cz3dZPkoks4fS/rq/QLB7/d7znOe7886/vjn55znOZGZSJLWt0+sdgGSpN4Z5pJUgGEuSQUY5pJUgGEuSQXcthoHHRkZybGxsdU4tCStW2+++eYPMrPVaduqhPnY2Bjnz59fjUNL0roVEf+y2DZPs0hSAYa5JBVgmEtSAYa5JBVgmEtSAYa5JBVgmEtSAYa5JBVgmEtSAatyBeh6Nfbky6ty3ItP7V+V40paP1yZS1IBhrkkFWCYS1IBhrkkFWCYS1IBhrkkFWCYS1IBhrkkFWCYS1IBhrkkFbBkmEfEnoh4oWnfGxHfioipiPi7iPh8M/56MzYVEXcNumhJ0o2WvDdLZp6NiMmmuwmYyMyfRMRjmflSM34iM6cGVqUk6WOt6EZbmfkGQER8GvjhvE27I2IY2AEcycyPFu7b/IMwCdBut7suWJJ0s27PmT8BnJ7XP56Zx4BpYKLTDpl5MjPHM3O81Wp1eVhJUicrDvOICODOzLze9IeA2WbzJWC0f+VJkpajm5X5Z7jx9Mw+4FDT3g7M9FqUJGlllvNplglgb0QcbFblQ8CP5j3kNHAtIg4AWzJzejClSpIWs5xPs7wG3D1v6NvAV+Zt/xA41ffKJEnL5kVDklSAYS5JBRjmklSAYS5JBRjmklSAYS5JBRjmklSAYS5JBRjmklSAYS5JBRjmklSAYS5JBRjmklSAYS5JBRjmklSAYS5JBRjmklSAYS5JBRjmklSAYS5JBSwZ5hGxJyJemNd/PSKmmp+7mrEjEXE4In5vkMVKkjpbMswz8yxwdd7Qicx8vPm5EBH3ANsy8zlgOCJ2DqpYSVJnt3Wxz+6IGAZ2AEeAh4Bzzba3gAeBtxfuFBGTwCRAu93uqlhJUmfdnDM/npnHgGlgAhgBPmi2XQXu6LRTZp7MzPHMHG+1Wl0VK0nqbEVhHhFDwGzTvQSMAu8Dm5uxzU1fknQLrXRlvg841LS3AzPAq8D9zdgu4ExfKpMkLdtyPs0yAeyNiIPA3wPXIuIAsCUzpzNzBrgcEYeB2aYvSbqFlnwDNDNfA+6eN3Sqw2Oe7mdRkqSV8aIhSSrAMJekAgxzSSrAMJekAgxzSSrAMJekAgxzSSrAMJekAgxzSSrAMJekAgxzSSrAMJekAgxzSSrAMJekAgxzSSrAMJekAgxzSSrAMJekApb82ri1ZuzJl1e7BElac5bzhc57IuKFpj0UEV+MiC9ExB9HxCea8dcjYqr5uWvQRUuSbrRkmGfmWeBq090HXM/MF4HvAfc24ycy8/Hm58JAKpUkLWqlp1nOAJ9q2tuAi017d0QMAzuAI5n50cIdI2ISmARot9vd1CpJWsSK3gDNzCuZ+W5E3AO8k5mzzabjmXkMmAYmFtn3ZGaOZ+Z4q9XqrWpJ0g1W/GmWiBgF7svMZ5v+EPCzUL8EjPatOknSsqwozCPiduCRzHw+IjZFxC7mzqMfah6yHZjpc42SpCUs59MsE8DeiDgI/BbwcERMAa8APwVOA9ci4gCwJTOnB1mwJOlmS74BmpmvAXfPGzre4WGn+laRJGnF1t1FQxvRal4odfGp/at2bEnL5+X8klSAYS5JBRjmklSAYS5JBRjmklSAYS5JBRjmklSAYS5JBRjmklSAYS5JBRjmklSAYS5JBRjmklSAYS5JBRjmklSAYS5JBRjmklSAYS5JBRjmklTAkt8BGhF7gK9m5qNN/whwBdiamc8sNiZJunWWXJln5lngKkBE3ANsy8zngOGI2NlpbKAVS5JusuTKfIGHgHNN+y3gQSA7jL29cMeImAQmAdrtdje1SpIWsdJz5iPAB037KnDHImM3ycyTmTmemeOtVqubWiVJi1jpyvx9YHPT3tz0o8OYJOkWWunK/FXg/qa9CzizyJgk6RZaMswjYgLYGxEHge8ClyPiMDCbmTOZObNwbLAlS5IWWvI0S2a+Btw9b+jpDo+5aUySdOt40ZAkFWCYS1IBhrkkFWCYS1IBhrkkFWCYS1IBhrkkFWCYS1IBhrkkFWCYS1IBhrkkFWCYS1IBhrkkFWCYS1IBhrkkFWCYS1IBhrkkFWCYS1IBhrkkFWCYS1IBS36hcycRcS/wl8A7wAjwF8AfAe81DzmamRf6UaAkaWldhTmwCZjIzJ9ExGOZ+VJE/HxmTvWzOEnS8nQV5pn5BkBEfBr4YTO8OyKGgR3Akcz8aP4+ETEJTAK02+2uC5Yk3azXc+ZPAKeb9vHMPAZMAxMLH5iZJzNzPDPHW61Wj4eVJM3XdZhHRAB3Zub1iBgCZptNl4DRfhQnSVqeXlbmn+H/T9PsAw417e3ATC9FSZJWppcwHwJ+1LRPA9ci4gCwJTOne65MkrRs3X6ahcz8NvCVpv0hcKpPNUmSVsiLhiSpgK5X5toYxp58eVWOe/Gp/atyXGm9cmUuSQUY5pJUgGEuSQUY5pJUgGEuSQUY5pJUgGEuSQUY5pJUgGEuSQUY5pJUgGEuSQUY5pJUgGEuSQUY5pJUgGEuSQUY5pJUgGEuSQV0/U1DEfE68F7TPQp8AbgCbM3MZ3ovTZK0XL2szE9k5uOZ+XjzPNsy8zlgOCJ29qc8SdJy9PIdoLsjYhjYAfwTcK4Zfwt4EHh7/oMjYhKYBGi32z0cVpK0UC8r8+OZeQyYBkaAD5rxq8AdCx+cmSczczwzx1utVg+HlSQt1FWYR8QQMNt0LwGbgM1NfzPwfu+lSZKWq9uV+T7gUNPeDnwduL/p7wLO9FaWJGklug3z08C1iDgAbMnMc8DliDgMzGbmTN8qlCQtqas3QDPzQ+DUgrGn+1KRJGnFvGhIkgowzCWpAMNckgowzCWpAMNckgowzCWpAMNckgowzCWpAMNckgowzCWpAMNckgowzCWpAMNckgowzCWpAMNckgowzCWpAMNckgowzCWpAMNckgowzCWpgK6+0DkihoDHgO8DDwBHgX8A3msecjQzL/SlQknSkroKc2AfcD0zX4yINnAvcCIzp/pWmTa0sSdfXrVjX3xq/6odW+pWt2F+BvhU094GXAS+GBHDwA7gSGZ+NH+HiJgEJgHa7XaXh5UkddLVOfPMvJKZ70bEPcA7mTkLHM/MY8A0MNFhn5OZOZ6Z461Wq7eqJUk36PoN0IgYBe7LzGebc+izzaZLwGg/ipMkLU9XYR4RtwOPZObzEbEJ+EPgULN5OzDTp/okScvQ7cr8S8DDETEFvAK8AFyLiAPAlsyc7leBkqSldfUGaGZ+DfjaguF/7r0cSVI3vGhIkgowzCWpAMNckgowzCWpAMNckgowzCWpAMNckgowzCWpAMNckgowzCWpgG7vZy6VtVpfjOGXYqgXrswlqQDDXJIKMMwlqQDPmUtaNb4/0T+uzCWpAMNckgowzCWpAM+ZS2uE549vndX6s4bB/Xm7MpekAvq6Mo+II8AVYGtmPtPP55Y0GKu5SlX/9G1lHhH3ANsy8zlgOCJ29uu5JUkfr58r84eAc037LeBB4O2fbYyISWCy6V6NiO+s4LlHgB/0o8h1xDlvDM55Y/i/Ocef9PQ8v7jYhn6G+QhwoWlfBT47f2NmngROdvPEEXE+M8d7K299cc4bg3PeGG7FnPv5Buj7wOamvbnpS5JugX6G+avA/U17F3Cmj88tSfoYfQvzzJwBLkfEYWC26fdLV6dn1jnnvDE4541h4HOOzBz0MSRJA+ZFQ5JUgGEuSQUY5pJUwJq/0dZGuUVARAwBjwHfBx4AjgJfpvjcI+JO4A8y83c2wu86In4NuB34deD3gScoPOeIGAUOAP/B3AUvJyj6uo6IPcBXM/PRpn/T63mQr/E1vTLfYLcI2Adcz8wXge8B42yMuT8A/NxG+F1HRAvYkZnfAH4buIPicwZ+A/irzHwJ+DcKv64z8yxzF0x2zK5Bv8bXdJjT+RYBVZ0B/rFpbwM+R/G5R8TDwNeb7kb4Xe8DxiLiy8CfAfupP+ezwJ9GxFbgV4Dd1J8zdH49D/Q1vtbDfAT4oGlfZW4lU1JmXsnMd5t/vd9h7hRY2bk3//3+cWb+dzO0EX7XvwD8a2YeA/4a2Er9OZ8HLgN/C/wU+CT15wydX88DfY2v9TDfULcIaALuvsx8lvpz3wXcFhGfA0aBH1N7vgDXgH9v2peA/6H+nH8XOM7cqnQncJ36c4bOf38H+nd6rYf5hrlFQETcDjySmc9HxCbgWxSee2Z+IzPPZOYZ4D+ZO91Sdr6NN4BfbdqjQFJ/zp8E/ivnrk78G+b+Qas+Z+icXQPNszUd5gO+RcBa8yXg4YiYAl5h7r+kpececx4Ffpm5YCs938z8JkAz518C/pzicwaeBX4zIj7P3Mr8BEXnHBETwN6IOAh8lwXzHHSeeTm/JBWwplfmkqTlMcwlqQDDXJIKMMwlqQDDXJIKMMwlqYD/BYfRpVAk8Hq9AAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "\n",
    "plt.hist(length)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "max_len = 100"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "class CRF(layers.Layer):\n",
    "    def __init__(self, label_size):\n",
    "        super(CRF, self).__init__()\n",
    "        #随机生成0，1范围内shape × shape的矩阵\n",
    "        self.trans_params = tf.Variable(\n",
    "            tf.random.uniform(shape=(label_size, label_size)), name=\"transition\")\n",
    "    #实现 Graph Execution\n",
    "    @tf.function\n",
    "    def call(self, inputs, labels, seq_lens):#使用crf来计算损失\n",
    "        #inputs: 一个形状为[batch_size, max_seq_len, num_tags] 的tensor,一般使用BILSTM处理之后输出转换为他要求的形状作为CRF层的输入\n",
    "        #labels：形状为[batch_size, max_seq_len]的矩阵，真实标签\n",
    "        #seq_lens：一个形状为 [batch_size] 的向量,表示每个序列的长度\n",
    "        #transition_params：转移矩阵\n",
    "        log_likelihood, self.trans_params = tfa.text.crf_log_likelihood(\n",
    "                                                inputs, labels, seq_lens,\n",
    "                                                transition_params=self.trans_params)\n",
    "        #损失\n",
    "        loss = tf.reduce_sum(-log_likelihood)\n",
    "        return loss"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Model: \"model\"\n",
      "__________________________________________________________________________________________________\n",
      "Layer (type)                    Output Shape         Param #     Connected to                     \n",
      "==================================================================================================\n",
      "input_ids (InputLayer)          [(None, 100)]        0                                            \n",
      "__________________________________________________________________________________________________\n",
      "embedding (Embedding)           (None, 100, 200)     12807600    input_ids[0][0]                  \n",
      "__________________________________________________________________________________________________\n",
      "bidirectional (Bidirectional)   (None, 100, 400)     641600      embedding[0][0]                  \n",
      "__________________________________________________________________________________________________\n",
      "dense (Dense)                   (None, 100, 16)      6416        bidirectional[0][0]              \n",
      "__________________________________________________________________________________________________\n",
      "target_ids (InputLayer)         [(None, 100)]        0                                            \n",
      "__________________________________________________________________________________________________\n",
      "input_lens (InputLayer)         [(None,)]            0                                            \n",
      "__________________________________________________________________________________________________\n",
      "crf (CRF)                       ()                   256         dense[0][0]                      \n",
      "                                                                 target_ids[0][0]                 \n",
      "                                                                 input_lens[0][0]                 \n",
      "==================================================================================================\n",
      "Total params: 13,455,872\n",
      "Trainable params: 13,455,872\n",
      "Non-trainable params: 0\n",
      "__________________________________________________________________________________________________\n",
      "None\n"
     ]
    }
   ],
   "source": [
    "K.clear_session()\n",
    "\n",
    "EPOCHS = 16\n",
    "BATCH_SIZE = 64\n",
    "EMBED_DIM = 200#词向量的维度\n",
    "HIDDEN_SIZE = 200#隐层状态的维数\n",
    "MAX_LEN = 100\n",
    "VOCAB_SIZE = len(vocab2idx)\n",
    "CLASS_NUMS = len(label2idx)\n",
    "\n",
    "inputs = layers.Input(shape=(MAX_LEN,), name='input_ids', dtype='int32')\n",
    "targets = layers.Input(shape=(MAX_LEN,), name='target_ids', dtype='int32')\n",
    "seq_lens = layers.Input(shape=(), name='input_lens', dtype='int32')\n",
    "\n",
    "#嵌入层 词汇表的大小；词向量的维度\n",
    "x = layers.Embedding(input_dim=VOCAB_SIZE, output_dim=EMBED_DIM,weights = [embeddings_matrix])(inputs)\n",
    "x = layers.Bidirectional(layers.LSTM(HIDDEN_SIZE, return_sequences=True))(x)\n",
    "logits = layers.Dense(CLASS_NUMS)(x)#全连接层 CLASS_NUMS：输出的单元个数，output_shape = (batch_size ,units),输入的形状input_shape =(batch_size,input_dim)\n",
    "loss = CRF(label_size=CLASS_NUMS)(logits, targets, seq_lens)\n",
    "\n",
    "model = models.Model(inputs=[inputs, targets, seq_lens], outputs=loss)\n",
    "\n",
    "print(model.summary())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "('Failed to import pydot. You must `pip install pydot` and install graphviz (https://graphviz.gitlab.io/download/), ', 'for `pydotprint` to work.')\n"
     ]
    }
   ],
   "source": [
    "from keras.utils import plot_model\n",
    "plot_model(model, to_file='model.png')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "class CustomLoss(keras.losses.Loss):\n",
    "    def call(self, y_true, y_pred):\n",
    "        loss, pred = y_pred\n",
    "        return loss\n",
    "\n",
    "# 自定义Loss\n",
    "#model.compile(loss=CustomLoss(), optimizer='adam')\n",
    "# 或者使用lambda表达式\n",
    "#配置训练方法时，告知训练时用的优化器、损失函数和准确率评测标准\n",
    "model.compile(loss = lambda y_true, y_pred: y_pred, optimizer='adam')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(555, 100) (555, 100)\n"
     ]
    }
   ],
   "source": [
    "#加载训练集 数据填充\n",
    "train_datas = sequence.pad_sequences(X_train, maxlen=MAX_LEN)\n",
    "train_labels = sequence.pad_sequences(y_train, maxlen=MAX_LEN)\n",
    "train_seq_lens = np.array([MAX_LEN] * len(train_labels))\n",
    "labels_ = np.ones(len(train_labels))\n",
    "# train_labels = keras.utils.to_categorical(train_labels, CLASS_NUMS)\n",
    "print(np.shape(train_datas), np.shape(train_labels))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/16\n",
      "8/8 [==============================] - 19s 2s/step - loss: 11366.3613 - val_loss: 2716.3120\n",
      "Epoch 2/16\n",
      "8/8 [==============================] - 7s 930ms/step - loss: 3416.3074 - val_loss: 2021.2549\n",
      "Epoch 3/16\n",
      "8/8 [==============================] - 7s 912ms/step - loss: 2588.6593 - val_loss: 1739.4138\n",
      "Epoch 4/16\n",
      "8/8 [==============================] - 7s 848ms/step - loss: 2237.1494 - val_loss: 1536.8348\n",
      "Epoch 5/16\n",
      "8/8 [==============================] - 7s 808ms/step - loss: 2029.3669 - val_loss: 1412.4010\n",
      "Epoch 6/16\n",
      "8/8 [==============================] - 7s 920ms/step - loss: 1765.6615 - val_loss: 1307.8461\n",
      "Epoch 7/16\n",
      "8/8 [==============================] - 9s 1s/step - loss: 1564.4168 - val_loss: 1219.3301\n",
      "Epoch 8/16\n",
      "8/8 [==============================] - 7s 893ms/step - loss: 1412.5656 - val_loss: 1134.4653\n",
      "Epoch 9/16\n",
      "8/8 [==============================] - 8s 1s/step - loss: 1322.4046 - val_loss: 1073.1025\n",
      "Epoch 10/16\n",
      "8/8 [==============================] - 9s 1s/step - loss: 1250.0865 - val_loss: 1004.7911\n",
      "Epoch 11/16\n",
      "8/8 [==============================] - 9s 1s/step - loss: 1115.7908 - val_loss: 952.9447\n",
      "Epoch 12/16\n",
      "8/8 [==============================] - 7s 923ms/step - loss: 1034.9776 - val_loss: 886.5591\n",
      "Epoch 13/16\n",
      "8/8 [==============================] - 7s 901ms/step - loss: 942.2270 - val_loss: 846.8165\n",
      "Epoch 14/16\n",
      "8/8 [==============================] - 7s 905ms/step - loss: 883.5769 - val_loss: 794.8213\n",
      "Epoch 15/16\n",
      "8/8 [==============================] - 6s 792ms/step - loss: 759.3366 - val_loss: 759.8312\n",
      "Epoch 16/16\n",
      "8/8 [==============================] - 6s 803ms/step - loss: 732.1243 - val_loss: 712.3415\n"
     ]
    }
   ],
   "source": [
    "#可以设定fit函数内validation_split参数为切分验证集的比例\n",
    "history = model.fit(x=[train_datas, train_labels, train_seq_lens], y=labels_, \n",
    "        validation_split = 0.1, batch_size=BATCH_SIZE, epochs=EPOCHS)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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mJgIuGaZVq1aMXXqVAAyMjIAeOONN/jb3/7GN998w4gRI7jiiit44oknyM3N5dJLL2W71xpRUFDAa6+9RkJCAgsXLmTr1q3MnTuX2bNns379+irjWLBgga9GZtnoJoCsLMpbtCDBahLGNGujR48GICnJ7RpbtWq197WNGzeSlJRERkYGDz/8MJ999hl33303N954IykpKbRs2RJwtYXp06eTlpbG73//e1SV1atXs2TJEtasWcO7775bZQyjRo3igw8+aKAtrDlLEgCJiezq0YNUH2VvY5qtG24Ar5mntlqVlYFXKwBgyBB4+OGI87/99tvce++99OrVixdffHFvkgju6F25ciUzZ85k1qxZPPDAAxx++OGUlpby+uuvc/PNNzNu3DgA1qxZw9ixY1m8eDFXX301ZWVlPP7441x33XX8+OOPdO/endLS0hptzzPPPEPfvn359ttvufTSS0lOTmbatGmkpqYydepU7r//fvr06RO2rK4sSXh29epFqtUkjGmWTjnlFE455RR+//vfA1BeXg5UThIjR47k3nvvpXXr1nTv3p3du3fTrl07CgsLeeCBB3j00Ud54YUXGDBgAAArVqzg8ssv31uTWLx4MSUlJYwZM4Ybb7wx6tjeffdd9ttvP0aPHk3Xrl157rnnuPLKK/noo4+4//77OfLII9m9ezdA2LK6siTh2dmrF+Tlwe7dEFTFNMbEWBVH/NHaXYeT1crKyvb2TQSPcpo3bx4TJ05k3LhxlJSUMHv2bFq1asX8+fMpKyvj2GOPrbSeSy65hLPOOosvvviCdevWcdpppwHw7bff1qgmsWjRIs4880wA+vbty6OPPgrAtddey3XXXUfXrl256667IpbVlXVce3b16gWq8O238Q7FGBMn27dv54MPPqBt27ZARY0CYMSIEUyZMoVhw4axfft2pk6dyptvvgnAli1bmDhxIiUlJbz88sscdthhTJo0iWOOOYYjjjiCa665htzcXHJzc7nyyiuZO3du1DENGjSINWvWAK4p64ADDkBVKS4uZsqUKYwcOZKZM2eGLasPliQ8u3r1chPW5GRMs7R9+3a++uor1q5dyyWXXAJU1CT27NnDwoUL+c1vfsPixYtJT08nOTmZ888/n9LSUrp06cIhhxzCQw89xHnnnce8efOYNWsWxx13HJMnT+b+++9n/PjxvPvuu+Tl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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig, ax = plt.subplots()#创建一个figure \n",
    "ax.plot(history.history['loss'], '-r',label='训练集 loss')\n",
    "ax.plot(history.history['val_loss'], '-b',label='验证集 loss')\n",
    "\n",
    "####打开网格\n",
    "ax.grid(True)\n",
    "\n",
    "####定义x, y轴的名称\n",
    "ax.set_xlabel('epoch',fontsize=15)\n",
    "ax.set_ylabel('loss',fontsize=15)\n",
    "\n",
    "####定义标题\n",
    "fig.suptitle('实验一：BiLSTM-CRF模型损失函数变化曲线',fontsize=15)\n",
    "\n",
    "####展示图例 legend loc=是用来定义图例的位置的，还有很多选择，大家可以自己尝试\n",
    "ax.legend(loc = 'upper right')\n",
    "\n",
    "plt.savefig(\"实验一：BiLSTM-CRF模型损失函数变化曲线.png\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[7607.94091796875,\n",
       " 3222.760009765625,\n",
       " 2563.130126953125,\n",
       " 2181.842041015625,\n",
       " 1903.728515625,\n",
       " 1694.1297607421875,\n",
       " 1536.3580322265625,\n",
       " 1407.2509765625,\n",
       " 1296.3050537109375,\n",
       " 1201.2607421875,\n",
       " 1101.86083984375,\n",
       " 1009.4688720703125,\n",
       " 925.9458618164062,\n",
       " 846.2838134765625,\n",
       " 767.3449096679688,\n",
       " 699.0057983398438]"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "history.history['loss']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[2716.31201171875,\n",
       " 2021.2548828125,\n",
       " 1739.413818359375,\n",
       " 1536.8348388671875,\n",
       " 1412.4010009765625,\n",
       " 1307.8460693359375,\n",
       " 1219.330078125,\n",
       " 1134.46533203125,\n",
       " 1073.1025390625,\n",
       " 1004.7910766601562,\n",
       " 952.9447021484375,\n",
       " 886.5591430664062,\n",
       " 846.8165283203125,\n",
       " 794.8212890625,\n",
       " 759.8312377929688,\n",
       " 712.3414916992188]"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "history.history['val_loss']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "WARNING:absl:Found untraced functions such as lstm_cell_1_layer_call_and_return_conditional_losses, lstm_cell_1_layer_call_fn, lstm_cell_2_layer_call_and_return_conditional_losses, lstm_cell_2_layer_call_fn, lstm_cell_1_layer_call_fn while saving (showing 5 of 10). These functions will not be directly callable after loading.\n",
      "WARNING:absl:Found untraced functions such as lstm_cell_1_layer_call_and_return_conditional_losses, lstm_cell_1_layer_call_fn, lstm_cell_2_layer_call_and_return_conditional_losses, lstm_cell_2_layer_call_fn, lstm_cell_1_layer_call_fn while saving (showing 5 of 10). These functions will not be directly callable after loading.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "INFO:tensorflow:Assets written to: output/BiLSTM_CRF_NER_MODEL\\assets\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "INFO:tensorflow:Assets written to: output/BiLSTM_CRF_NER_MODEL\\assets\n"
     ]
    }
   ],
   "source": [
    "# 保存\n",
    "model.save(\"output/BiLSTM_CRF_NER_MODEL\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [],
   "source": [
    "model = models.load_model(\"output/BiLSTM_CRF_NER_MODEL\", compile=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [],
   "source": [
    "import json\n",
    "with open('output/idx2vocab.json',\"w\",encoding=\"utf-8\") as f:\n",
    "    json.dump(idx2vocab,f)\n",
    "    \n",
    "with open('output/vocab2idx.json',\"w\",encoding=\"utf-8\") as f:\n",
    "    json.dump(vocab2idx,f)\n",
    "\n",
    "with open('output/idx2label.json',\"w\",encoding=\"utf-8\") as f:\n",
    "    json.dump(idx2label,f)\n",
    "\n",
    "with open('output/label2idx.json',\"w\",encoding=\"utf-8\") as f:\n",
    "    json.dump(label2idx,f)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "# model = models.load_model(\"output/bilstm_crf_ner\", compile=False)\n",
    "# 如果需要继续训练，需要下面的重新compile\n",
    "# model.compile(loss=lambda y_true, y_pred: y_pred, optimizer='adam')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "#提取转移矩阵参数\n",
    "trans_params = model.get_layer('crf').get_weights()[0]\n",
    "# 获得BiLSTM的输出logits\n",
    "sub_model = models.Model(inputs=model.get_layer('input_ids').input,\n",
    "                        outputs=model.get_layer('dense').output)\n",
    "\n",
    "def predict(model, inputs, input_lens):\n",
    "    logits = sub_model.predict(inputs)\n",
    "    # 获取CRF层的转移矩阵\n",
    "    # crf_decode：viterbi解码获得结果\n",
    "    pred_seq, viterbi_score = tfa.text.crf_decode(logits, trans_params, input_lens)\n",
    "    return pred_seq"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 预测"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [],
   "source": [
    "with open('output/idx2vocab.json',encoding=\"utf-8\") as file_obj:\n",
    "    idx2vocab=json.load(file_obj)\n",
    "    \n",
    "with open('output/vocab2idx.json',encoding=\"utf-8\") as file_obj:\n",
    "    vocab2idx=json.load(file_obj)\n",
    "    \n",
    "with open('output/idx2label.json',encoding=\"utf-8\") as file_obj:\n",
    "    idx2label=json.load(file_obj)\n",
    "    \n",
    "with open('output/label2idx.json',encoding=\"utf-8\") as file_obj:\n",
    "    label2idx=json.load(file_obj)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [],
   "source": [
    "temp_idx2vocab = {}\n",
    "for k,v in idx2vocab.items():\n",
    "    temp_idx2vocab[int(k)] = v\n",
    "idx2vocab = temp_idx2vocab.copy()\n",
    "\n",
    "temp_idx2label = {}\n",
    "for k,v in idx2label.items():\n",
    "    temp_idx2label[int(k)] = v\n",
    "idx2label = temp_idx2label.copy()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [],
   "source": [
    "def pre(X_test,idx2label,model,idx2vocab,vocab2idx):\n",
    "    result=[]\n",
    "    maxlen = MAX_LEN\n",
    "    for item in X_test:\n",
    "        sentence=''\n",
    "        for i in item:\n",
    "            sentence+=idx2vocab[i]\n",
    "        print(sentence)\n",
    "        sent_chars = list(sentence)\n",
    "        sent2id = [vocab2idx[word] if word in vocab2idx else vocab2idx['UNK'] for word in sent_chars]\n",
    "        sent2id_new = np.array([[0] * (maxlen-len(sent2id)) + sent2id[:maxlen]])\n",
    "        test_lens = np.array([MAX_LEN])\n",
    "        pred_seq = predict(model, sent2id_new, test_lens)\n",
    "        y_label = pred_seq.numpy().reshape(1, -1)[0]\n",
    "        y_ner = [idx2label[i] for i in y_label][-len(sent_chars):]\n",
    "        print(y_ner)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [],
   "source": [
    "#测试集效果检测\n",
    "test_text = []\n",
    "for item in X_test:\n",
    "    sen=''\n",
    "    for i in item:\n",
    "        sen+=idx2vocab[i]\n",
    "    test_text.append(sen)\n",
    "test_label=[]\n",
    "for item in y_test:\n",
    "    test_label.append([idx2label[i] for i in item])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [],
   "source": [
    "filename='test_text.json'\n",
    "with open(filename,'w',encoding='gbk') as file_obj:\n",
    "    json.dump(test_text,file_obj,ensure_ascii=False,indent = 4)\n",
    "    \n",
    "filename='test_label.json'\n",
    "with open(filename,'w',encoding='gbk') as file_obj:\n",
    "    json.dump(test_label,file_obj,ensure_ascii=False,indent = 4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [],
   "source": [
    "def pre_data(sentence,vocab2idx,idx2label):\n",
    "    maxlen=MAX_LEN\n",
    "    sent_chars = list(sentence)\n",
    "    sent2id = [vocab2idx[word] if word in vocab2idx else vocab2idx['UNK'] for word in sent_chars]\n",
    "    sent2id_new = np.array([[0] * (maxlen-len(sent2id)) + sent2id[:maxlen]])\n",
    "    test_lens = np.array([MAX_LEN])\n",
    "\n",
    "    pred_seq = predict(model, sent2id_new, test_lens)\n",
    "    y_label = pred_seq.numpy().reshape(1, -1)[0]\n",
    "    y_ner = [idx2label[i] for i in y_label][-len(sent_chars):]\n",
    "    return y_ner"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "E:\\download\\anaconda\\download\\lib\\site-packages\\tensorflow_addons\\text\\crf.py:543: UserWarning: CRF Decoding does not work with KerasTensors in TF2.4. The bug has since been fixed in tensorflow/tensorflow##45534\n",
      "  warnings.warn(\n"
     ]
    }
   ],
   "source": [
    "pre_result=[]\n",
    "count=0\n",
    "for sen in test_text:\n",
    "    #print(sen)\n",
    "    result=pre_data(sen,vocab2idx,idx2label)\n",
    "    #print(result)\n",
    "    pre_result.append(result)\n",
    "    count+=1\n",
    "    # print(count)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [],
   "source": [
    "filename='pre_result.json'\n",
    "with open(filename,'w',encoding='gbk') as file_obj:\n",
    "    json.dump(pre_result,file_obj,ensure_ascii=False,indent = 4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "pre_ = []\n",
    "for s in pre_result:\n",
    "    for c in s:\n",
    "        pre_.append(c)\n",
    "        \n",
    "test_ = []\n",
    "for s in y_test:\n",
    "    for c in s:\n",
    "        test_.append(idx2label[c])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3025"
      ]
     },
     "execution_count": 40,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(pre_)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3025"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(test_)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['B-DRU', 'E-DRU', 'O']"
      ]
     },
     "execution_count": 42,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pre_[:3]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['B-DRU', 'E-DRU', 'O']"
      ]
     },
     "execution_count": 43,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "test_[:3]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "              precision    recall  f1-score   support\n",
      "\n",
      "       B-DRU     0.7906    0.7906    0.7906       191\n",
      "      B-NAME     0.4722    0.6800    0.5574        25\n",
      "       B-QUA     0.8636    0.8941    0.8786        85\n",
      "       B-SYM     0.4608    0.6438    0.5371        73\n",
      "       B-USE     0.7059    0.7647    0.7341       204\n",
      "       E-DRU     0.7632    0.8580    0.8078       169\n",
      "      E-NAME     0.4167    0.9375    0.5769        16\n",
      "       E-QUA     0.9205    0.9419    0.9310        86\n",
      "       E-SYM     0.5800    0.7250    0.6444        80\n",
      "       E-USE     0.6920    0.8031    0.7434       193\n",
      "       I-DRU     0.2174    0.8333    0.3448         6\n",
      "      I-NAME     0.9020    0.6389    0.7480        72\n",
      "       I-QUA     0.3000    0.6667    0.4138         9\n",
      "       I-SYM     0.5272    0.6467    0.5808       150\n",
      "       I-USE     0.8185    0.7677    0.7923       693\n",
      "           O     0.9196    0.7760    0.8417       973\n",
      "\n",
      "    accuracy                         0.7742      3025\n",
      "   macro avg     0.6469    0.7730    0.6827      3025\n",
      "weighted avg     0.7995    0.7742    0.7816      3025\n",
      "\n"
     ]
    }
   ],
   "source": [
    "from sklearn.metrics import classification_report\n",
    "print(classification_report(pre_,test_,digits=4))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "图"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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PPHEEzX2uaqrbieJjh0rV26M8QevL5GEhBrrS+zVqxeJiYmAa5Zp2LAh48ePByA9PR2Ad955h4cffpiff/6Zww47jCuuuIJnnnmGrKwsLrvsMrZu3QpAbm4ukyZNIiEhgdmzZ7Np0yamT5/O1KlTycnJKTeOWbNm+WpkVt3ukxg1anefxG6NGrlyY4w/BfUlAjXalzho0CAAkpLcobFhw4a7n1u7di1JSUmkp6fz2GOP8e233zJy5EhuuukmkpOTqV+/PuBqC++99x6pqancddddqCorVqxg3rx5rFy5ko8//rjcGAYOHMjnn39erc9Rk+p2kgj8Q915J7piBdKwIYwbZ53WxsTTjTeC18wT1jffQEFB2bIdO+Dyy+H55wFoWFwMXq0AgD594LHHIu7ygw8+YNSoUXTq1InXXnttd5II7uhdtmwZkydPZsqUKTzyyCMcddRRFBYW8vbbb3PLLbcwdOhQAFauXMmQIUOYO3cuV199NcXFxTz99NNcf/31/Prrr7Rv357CwsJK/ELg+eefp1u3bvzyyy9cdtllNGjQgIkTJ5KSksL48eN58MEH6dq1a9iy6qrbzU3gEsLy5awfOBDatLEEYYzfhSaIisqjcNppp/H111/TrVs3AEpKSoCySeKII45g1KhRPProo3To0IGdO3eSkpICwCOPPMJhhx3GwoUL6dKlC4mJiSxatIjhw4czYsQI3n33XYYPH84dd9zBQw89VKnYPv74Y/bbbz8GDRpEVlYWL730EgBffvklWVlZPP/887trPOHKqqtu1ySCbO/UCbKzYedOqKFfrjGmCso54wdcH8SKFXuWB77DwM5qTFYrLi7e3TcRPMppxowZjB49mqFDh1JQUMDUqVNp2LAhM2fOpLi4mOOOO67Mfi699FLOOeccvv/+e1avXs0ZZ5wBwC+//FKpmsScOXM4++yzAejWrRtPPvkkANdddx3XX389bdu25d57741YVl1Wk/Ds6NQJVOGXX+IdijGmPKNGub7DYDXUl7h161Y+//xzmjRpApTWKAAOO+wwXnzxRfr168fWrVsZP3487777LgAbNmxg9OjRFBQU8MYbb3DkkUfy+OOPM2DAAI4++miuvfZasrKyyMrK4sorr2T69OlRx9SrVy9WeiMuV65cyYEHHoiqkp+fz4svvsgRRxzB5MmTw5bVBEsSnh2dOrk7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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig, ax = plt.subplots()#创建一个figure \n",
    "ax.plot(history.history['loss'], '-r',marker = \"o\", label='训练集 loss')\n",
    "ax.plot(history.history['val_loss'], '-b',marker = \"*\",label='验证集 loss')\n",
    "\n",
    "####打开网格\n",
    "ax.grid(True)\n",
    "\n",
    "####定义x, y轴的名称\n",
    "ax.set_xlabel('epoch',fontsize=15)\n",
    "ax.set_ylabel('loss',fontsize=15)\n",
    "\n",
    "####定义标题\n",
    "fig.suptitle('实验一：BiLSTM-CRF模型损失函数变化曲线',fontsize=15)\n",
    "\n",
    "####展示图例 legend loc=是用来定义图例的位置的，还有很多选择，大家可以自己尝试\n",
    "ax.legend(loc = 'upper right')\n",
    "\n",
    "plt.savefig(\"实验一：BiLSTM-CRF模型损失函数变化曲线.png\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
